home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / wtjmarch.zip / LIFE.ZIP / CLIFE.CPP < prev    next >
C/C++ Source or Header  |  1992-01-27  |  19KB  |  539 lines

  1. /****************************************************/
  2. /*                      Life                        */
  3. /*                 written in C++                   */
  4. /*                Copyright (c) 1991                */
  5. /*                  Zack Urlocker                   */
  6. /*  Translated from Pascal to C++ by Bruce Eckel    */
  7. /*                    01/27/92                      */
  8. /****************************************************/
  9.  
  10. /* This is a simple implementation of the Game of Life originally
  11.   written in Turbo Pascal for Windows using the ObjectWindows
  12.   application framework.  The program is divided into three main
  13.   object types:
  14.  
  15.   TLifeCells        --mutates and draws the cells in the window
  16.   TLifeWindow       --responds to Windows messages, menu commands,
  17.               keyboard and mouse events
  18.   TLifeApplication  --creates and shows the main window
  19. */
  20.  
  21. #include <owl.h>
  22. #include <inputdia.h>
  23. #include <string.h>
  24. #include <time.h>   // for randomize()
  25. #include <stdlib.h> // for rand() and randomize()
  26.  
  27. const
  28.   cm_Clear   = 201,        /* command menu constant IDs */
  29.   cm_Go      = 202,
  30.   cm_Trace   = 203,
  31.   cm_Stop    = 204,
  32.   cm_Exit    = 209,
  33.   cm_About   = 210,
  34.   cm_Timer   = 301,
  35.   cm_Grid    = 302,
  36.   cm_Zoom    = 303,
  37.   cm_Random  = 401,
  38.   cm_Bloom   = 402,
  39.   cm_Walker  = 403,
  40.   cm_Help    = 501,
  41.   cm_CmdMode = 601,      /* For Lotus style slash (/) key commands */
  42.  
  43.   XMax =     100,        /* Theoretical maximum matrix size */
  44.   YMax =     100;        /* Actual size is based on zoom */
  45.  
  46. const unsigned   
  47.   Black = 0x000000,       /* Windows color constants */
  48.   White = 0xFFFFFF,
  49.   Blue  = 0xFF0000,
  50.   Red   = 0x0000FF;
  51.  
  52. const Boolean            // (Boolean defined in header files)
  53.   Dead =   False,        /* cell values */
  54.   Born =    True;
  55.  
  56. typedef Boolean matrix[XMax + 1][YMax + 1];
  57.  
  58.  
  59. // The cells are responsible for mutating and drawing in a window.
  60. // The cells will be notified whenever the size of the grid or
  61. // number of rows and columns in the window changes.
  62. class TLifeCells : public Object {
  63.     matrix cells;                        /* actual cells        */
  64.     matrix scratchCells;                 /* scratch work area   */
  65.     int C_rows;                          /* visible rows        */
  66.     int C_cols;                          /* visible columns     */
  67.     int gridSize;                        /* for drawing a cell  */
  68. public:
  69.     TLifeCells() { ClearCells(); }       /* initialize cells    */
  70.     void ClearCells() {                   /* Set all to dead     */
  71.       memset(&cells, 0, sizeof(cells));
  72.       memset(&scratchCells, 0, sizeof scratchCells);
  73.     }
  74.     void mutate(HDC DC);                 /* mutate all cells    */
  75.     void draw(HDC DC);                   /* draw all cells      */
  76.     // Set the cell to born or dead state:
  77.     void setCell(int i, int j, Boolean State) { cells[i][j] = State; };
  78.     // Is the cell alive? :
  79.     Boolean aliveCell(int i, int j) { return cells[i][j]; };
  80.     void walker(int i, int j);
  81.     void bloom(int i, int j);
  82.     void mutateCell(HDC DC, int i, int j);
  83.     void drawCell(HDC DC, int i, int j, Boolean alive);
  84.     // Dummy definitions for Object pure virtual functions:
  85.     classType isA() const { return 0; }
  86.     char * nameOf() const { return "TLifeCells"; }
  87.     hashValueType   hashValue() const { return 0; }
  88.     int             isEqual( const Object& ) const { return 0; }
  89.     void            printOn( ostream& ) const {}
  90.     friend class TLifeWindow;  // give it access to private members
  91. };
  92.  
  93. // The window responds to messages and controls the game board:
  94. class TLifeWindow : public TWindow {
  95.   TLifeCells cells;            /* cells being mutated */
  96.   int speed;                   /* timer speed         */
  97.   Boolean running;             /* is timer running?   */
  98.   int rows;                    /* visible rows        */
  99.   int cols;                    /* visible columns     */
  100.   Boolean grid;                /* is grid turned on?  */
  101.   int gridSize;                /* for drawing a cell  */
  102.   Boolean mouseDown;           /* is mouse down?      */
  103.   int xDown;                   /* x location in grid  */
  104.   int yDown;                   /* y location in grid  */
  105.   HDC mutateDC;                /* draw each mutation  */
  106.   HDC mouseMoveDC;             /* draw mouse moves    */
  107. public:
  108.   TLifeWindow(TWindowsObject* Parent, char* ATitle);
  109.   virtual void GetWindowClass(WNDCLASS& WndClass);
  110.  
  111.     /* menu response methods */
  112.   virtual void Clear(TMessage& Msg) = [ CM_FIRST + cm_Clear ];
  113.   virtual void Randomize(TMessage& Msg) = [ CM_FIRST + cm_Random ];
  114.   virtual void Bloom(TMessage& Msg) = [ CM_FIRST + cm_Bloom ];
  115.   virtual void Walker(TMessage& Msg) = [ CM_FIRST + cm_Walker ];
  116.   virtual void Go(TMessage& Msg) = [ CM_FIRST + cm_Go ];
  117.   // Single step by stopping the timer and then mutate once:
  118.   virtual void Trace(TMessage& Msg) = [ CM_FIRST + cm_Trace ] {
  119.     TLifeWindow::Stop(Msg);  // directly calling a message response method!
  120.     TLifeWindow::WMTimer(Msg);
  121.   }
  122.   virtual void Stop(TMessage& Msg) = [ CM_FIRST + cm_Stop ];
  123.   virtual void Exit(TMessage&) // Exit the program
  124.     = [ CM_FIRST + cm_Exit ] { PostQuitMessage(0); }
  125.   virtual void About(TMessage& ) // Display About box
  126.     = [ CM_FIRST + cm_About ] {
  127.       TDialog *Dl = new TDialog(this, "AboutDlg");
  128. //      Dl->Execute();
  129.     GetApplication()->ExecDialog(Dl);
  130.   }
  131.   virtual void Timer(TMessage& Msg) = [ CM_FIRST + cm_Timer ];
  132.   virtual void GridToggle(TMessage& Msg) = [ CM_FIRST + cm_Grid ];
  133.   virtual void Zoom(TMessage& Msg) = [ CM_FIRST + cm_Zoom ];
  134.   virtual void Help(TMessage&) = [ CM_FIRST + cm_Help ] {
  135.     TDialog *Dl = new TDialog(this, "HelpDlg");
  136. //    Dl->Execute();
  137.     GetApplication()->ExecDialog(Dl);
  138.   }
  139.   // Respond to Lotus style commands from slash (/) accelerator:
  140.   virtual void CmdMode(TMessage&) = [ CM_FIRST + cm_CmdMode ] {
  141.     SendMessage(HWindow, WM_SYSCOMMAND, 0xF100, 0);
  142.   }
  143.   virtual void Paint(HDC DC, PAINTSTRUCT& PaintInfo);
  144.   void DrawGrid(HDC DC);
  145.       /* windows message response methods */
  146.   // Ensure that cursor is visible even when no mouse:
  147.   virtual void WMSetFocus(TMessage&)
  148.     = [ WM_FIRST + WM_SETFOCUS ] { ShowCursor(True); }
  149.   // Return cursor to previous state for other windows:
  150.   virtual void WMKillFocus(TMessage&)
  151.     = [ WM_FIRST + WM_KILLFOCUS ] { ShowCursor(False); }
  152.   virtual void WMKeyDown(TMessage& Msg) = [ WM_FIRST + WM_KEYDOWN ];
  153.   virtual void WMLButtonDown(TMessage& Msg) = [ WM_FIRST + WM_LBUTTONDOWN ];
  154.   virtual void WMLButtonUp(TMessage& Msg) = [ WM_FIRST + WM_LBUTTONUP ];
  155.   virtual void WMMouseMove(TMessage& Msg) = [ WM_FIRST + WM_MOUSEMOVE ];
  156.   // Zoom when right mouse button is pressed:
  157.   virtual void WMRButtonDown(TMessage& Msg)
  158.     = [WM_FIRST + WM_RBUTTONDOWN] { TLifeWindow::Zoom(Msg); }
  159.   virtual void WMTimer(TMessage& Msg) = [ WM_FIRST + WM_TIMER ];
  160.   virtual void WMSize(TMessage& Msg) = [ WM_FIRST + WM_SIZE ];
  161.   // When the window is destroyed, stop any timers:
  162.   virtual void WMDestroy(TMessage& Msg) = [ WM_FIRST + WM_DESTROY ] {
  163.     KillTimer(HWindow, 1);
  164.     TWindow::WMDestroy(Msg);
  165.   }
  166. };
  167.  
  168. /*--------------------------------------------------*/
  169. /* TLifeCell's method implementations:              */
  170. /*--------------------------------------------------*/
  171.  
  172. void TLifeCells::walker(int i, int j) {
  173.   cells[i][j+2] = Born;
  174.   cells[i+1][j+2] = Born;
  175.   cells[i+2][j+2] = Born;
  176.   cells[i+2][j+1] = Born;
  177.   cells[i+1][j] = Born;
  178. }
  179.  
  180. void TLifeCells::bloom(int i, int j) {
  181.   cells[i+1][j] = Born;
  182.   cells[i][j+1] = Born;
  183.   cells[i][j+2] = Born;
  184.   cells[i][j+3] = Born;
  185.   cells[i+1][j+3] = Born;
  186.   cells[i+2][j+3] = Born;
  187.   cells[i+2][j+2] = Born;
  188.   cells[i+2][j+1] = Born;
  189. }
  190.  
  191. /* Draw a single cell as a borderless rectangle */
  192. void TLifeCells::drawCell(HDC DC, int i, int j,  Boolean alive) {
  193. /* There was an off-by-one bug here previously */
  194.   int xScreen = i * gridSize;
  195.   int yScreen = j * gridSize;
  196.   int brushcolor = (alive ? BLACK_BRUSH : WHITE_BRUSH);
  197.   SelectObject(DC, GetStockObject(brushcolor));
  198.   Rectangle(DC, xScreen+1, yScreen+1, xScreen+gridSize, yScreen+gridSize);
  199.   DeleteObject(SelectObject(DC, GetStockObject(BLACK_BRUSH)));
  200. }
  201.  
  202. /* Redraw active cells on screen */
  203. void TLifeCells::draw(HDC DC) {
  204.   for(int i= 1 ; i <= C_cols ; i++ )
  205.     for(int j = 1 ; j <= C_rows ; j++)
  206.       if (cells[i][j])
  207.     drawCell(DC, i, j, Born);
  208. }
  209.  
  210. /* Determine how the cell should mutate by the number of neighbors
  211.   it has.  Too few or too many means it should die.  */
  212. void TLifeCells::mutateCell(HDC DC, int i, int j) {
  213.   int neighbors = 0;
  214.   if (cells[i-1][j])   ++neighbors;
  215.   if (cells[i+1][j])   ++neighbors;
  216.   if (cells[i][j-1])   ++neighbors;
  217.   if (cells[i][j+1])   ++neighbors;
  218.   if (cells[i-1][j-1]) ++neighbors;
  219.   if (cells[i+1][j+1]) ++neighbors;
  220.   if (cells[i-1][j+1]) ++neighbors;
  221.   if (cells[i+1][j-1]) ++neighbors;
  222.  
  223.   if ( cells[i][j] == False )   /* it's a dead cell */
  224.     if (neighbors == 3) {       /* bring it to life */
  225.       scratchCells[i][j] =  Born;
  226.       drawCell(DC, i, j, Born);
  227.     }
  228.     else
  229.       scratchCells[i][j] = cells[i][j];
  230.   else                         /* it's a live cell */
  231.     if ((neighbors < 2) || (neighbors > 3)) {  /* kill it */
  232.       scratchCells[i][j] = Dead;
  233.       drawCell(DC, i, j, Dead);
  234.     }
  235.     else
  236.       scratchCells[i][j] = cells[i][j];
  237. }
  238.  
  239. /* Mutate all of the visible cells */
  240. void TLifeCells::mutate(HDC DC) {
  241.   for (int i = 1; i <= C_cols; i++)
  242.     for (int j = 1; j <= C_rows; j++ )
  243.       mutateCell(DC, i, j);
  244.   /* update the real matrix */
  245.   memcpy(cells, scratchCells, sizeof(cells));
  246. }
  247.  
  248. /*--------------------------------------------------*/
  249. /* TLifeWindow's method implementations:            */
  250. /*--------------------------------------------------*/
  251. /* Initialize all fields to starting values */
  252. TLifeWindow::TLifeWindow(TWindowsObject* AParent, char* ATitle) :
  253.   TWindow(AParent, ATitle) {
  254.   running = False;
  255.   speed = 500;
  256.   grid = True;
  257.   gridSize = 20;
  258.   cells.gridSize = 20;
  259.   mouseDown = False;
  260.   Attr.W=400;          /* Force window size */
  261.   Attr.H=300;
  262. }
  263.  
  264. /* Override default cursor, icon, menu */
  265. void TLifeWindow::GetWindowClass(WNDCLASS& WndClass) {
  266.   TWindow::GetWindowClass(WndClass);  // call base-class version
  267.   WndClass.style = 0;
  268.   WndClass.hCursor = LoadCursor(WndClass.hInstance, "LifeCur");
  269.   WndClass.hIcon = LoadIcon(WndClass.hInstance, "LifeIco");
  270.   WndClass.lpszMenuName = "LifeMenu";
  271. }
  272.  
  273. // Respond to a timer tick by mutating the cells.
  274. // This is the main activity of the program.
  275. void TLifeWindow::WMTimer(TMessage& ) {
  276.   mutateDC = GetDC(HWindow);
  277.   // Use a white pen for the border, then set it back when done:
  278.   SelectObject(mutateDC, GetStockObject(WHITE_PEN));
  279.   cells.mutate(mutateDC);
  280.   SelectObject(mutateDC, GetStockObject(BLACK_PEN));
  281.   ReleaseDC(HWindow, mutateDC);
  282. }
  283.  
  284. /* Randomly create a starting pattern */
  285. void TLifeWindow::Randomize(TMessage& Msg) {
  286.   TLifeWindow::Clear(Msg);
  287.   for (int i= 1; i <= cols; i++ )
  288.     for (int j = 1 ; j <= rows ; j++ )
  289.       if (random(100) < 25 )
  290.     cells.setCell(i, j, Born);
  291.   InvalidateRect(HWindow, 0, True);
  292. }
  293.  
  294. inline int odd(int arg) { return arg % 2; }  // is arg odd?
  295.  
  296. /* Create a non-random starting pattern */
  297. void TLifeWindow::Bloom(TMessage& Msg) {
  298.   TLifeWindow::Clear(Msg);
  299.   for (int i = 0 ; i <= cols / 7 ; i++)
  300.     for (int j = 0 ; j <= rows / 7 ; j++)
  301.     if ( !odd(i+j) )
  302.       cells.bloom(4+i*7, 2+j*7);
  303.   InvalidateRect(HWindow, 0, True);
  304. }
  305.  
  306. /* Create a non-random starting pattern */
  307. void TLifeWindow::Walker(TMessage& Msg) {
  308.   TLifeWindow::Clear(Msg);
  309.   for (int i = 0 ; i <= cols / 7 ; i++)
  310.     for (int j = 0 ; j <= rows / 7 ; j++)
  311.     if ( !odd(i+j) )
  312.       cells.walker(2+i*7, 2+j*7);
  313.   InvalidateRect(HWindow, 0, True);
  314. }
  315.  
  316. /* Start the timer and update the menus */
  317. void TLifeWindow::Go(TMessage& ) {
  318.   if (SetTimer(HWindow, 1, speed, 0) != 0 )  {
  319.     running = True;
  320.     ModifyMenu(GetMenu(HWindow), cm_Go, MF_BYCOMMAND | MF_GRAYED,
  321.            cm_Go, "&Go" "\0x9" "^G");
  322.     ModifyMenu(GetMenu(HWindow), cm_Stop, MF_BYCOMMAND | MF_ENABLED,
  323.            cm_Stop,  "&Stop" "\0x9" "^S");
  324.   }  else  {
  325.     running = False;
  326.     MessageBeep(0);
  327.     MessageBox(HWindow, "No timers left to run Life;" "\0xD"
  328.             "Close some windows and retry!" ,
  329.             "Error", MB_OK + MB_ICONSTOP);
  330.   }
  331. }
  332.  
  333. /* Stop the timers and update the menus */
  334. void TLifeWindow::Stop(TMessage& ) {
  335.   ModifyMenu(GetMenu(HWindow), cm_Go, MF_BYCOMMAND | MF_ENABLED,
  336.          cm_Go, "&Go" "\0x9"  "^G");
  337.   ModifyMenu(GetMenu(HWindow), cm_Stop, MF_BYCOMMAND | MF_GRAYED,
  338.          cm_Stop,  "&Stop" "\0x9" "^S");
  339.   running = False;
  340.   KillTimer(HWindow, 1);
  341. }
  342.  
  343. /* Stop current timer, prompt for new speed, restart */
  344. void TLifeWindow::Timer(TMessage& Msg) {
  345.   TLifeWindow::Stop(Msg);
  346.   const sz = 10; char InputText[sz];
  347.   itoa(speed, InputText, sz); // initialize buffer with current speed
  348.   TInputDialog* InDlg = new TInputDialog(this,
  349.     "Timer Speed", "Input new time (milliseconds):",
  350.     InputText, sizeof(InputText));
  351.   if (InDlg->Execute()  == IDOK )  {
  352.     int NewSpeed = atoi(InputText);
  353.     if(NewSpeed > 0)
  354.       speed = NewSpeed;
  355.   } else
  356.   MessageBeep(0);
  357.   TLifeWindow::Go(Msg); // start timers
  358. }
  359.  
  360. /* Stop, clear the matrix, restart */
  361. void TLifeWindow::Clear(TMessage& Msg) {
  362.   Boolean paused = running;
  363.   TLifeWindow::Stop(Msg);  // stop timers
  364.   cells.ClearCells();
  365.   InvalidateRect(HWindow, 0, True);
  366.   if (paused)
  367.     TLifeWindow::Go(Msg); // start timers
  368. }
  369.  
  370. /* Toggle the displaying of the grid and redraw */
  371. void TLifeWindow::GridToggle(TMessage& ) {
  372.   unsigned style;
  373.   grid = (Boolean)!grid;
  374.   if(grid)
  375.     style = MF_CHECKED;
  376.   else
  377.     style = MF_UNCHECKED;
  378.   CheckMenuItem(GetMenu(HWindow), cm_Grid, style);
  379.   DrawMenuBar(HWindow);
  380.   InvalidateRect(HWindow, 0, True);
  381. }
  382.  
  383. /* Zoom the display, update internal info then redraw */
  384. void TLifeWindow::Zoom(TMessage& ) {
  385.   gridSize = gridSize * 2;
  386.   if (gridSize > 50 )
  387.     gridSize = 10;
  388.   cols = Attr.W / gridSize;
  389.   rows = Attr.H / gridSize;
  390.   /* update the cells */
  391.   cells.C_rows = rows;
  392.   cells.C_cols = cols;
  393.   cells.gridSize = gridSize;
  394.   InvalidateRect(HWindow, 0, True);
  395. }
  396.  
  397. /* Draw the grid and the cells */
  398. void TLifeWindow::Paint(HDC DC, PAINTSTRUCT& ) {
  399.   SelectObject(DC, GetStockObject(BLACK_PEN));
  400.   if (grid) DrawGrid(DC);
  401.   SelectObject(DC, GetStockObject(WHITE_PEN));
  402.   cells.draw(DC);
  403. }
  404.  
  405. /* Draw the grid background. */
  406. void TLifeWindow::DrawGrid(HDC DC) {
  407.   for (int i = 1; i <= rows; i++)  {
  408.     MoveTo(DC, 0, i*gridSize);
  409.     LineTo(DC, Attr.W, i*gridSize);
  410.   }
  411.   for (i = 1; i <= cols; i++)  {
  412.     MoveTo(DC, i*gridSize, 0);
  413.     LineTo(DC, i*gridSize, Attr.H);
  414.   }
  415. }
  416.  
  417. /* Use keyboard to simulate mouse events.  Accelerator keys
  418.   are handled as response methods. */
  419. void TLifeWindow::WMKeyDown(TMessage& Msg) {
  420.   int x, y;
  421.   POINT pos;
  422.   /* Determine position of cursor in Window */
  423.   GetCursorPos(&pos);
  424.   ScreenToClient(HWindow, &pos);
  425.   x=pos.x;
  426.   y=pos.y;
  427.   /* move the cursor position */
  428.   switch(Msg.WParam) {
  429.     case VK_UP    : y = y - gridSize;  break;
  430.     case VK_DOWN  : y = y + gridSize;  break;
  431.     case VK_RIGHT : x = x + gridSize;  break;
  432.     case VK_LEFT  : x = x - gridSize;  break;
  433.     case VK_HOME  : x = gridSize / 2;
  434.             y = gridSize / 2;
  435.             break;
  436.     case VK_END :   x = Attr.W - gridSize / 2;
  437.             y = Attr.H - gridSize / 2;
  438.             break;
  439.     case VK_RETURN :
  440.     case VK_SPACE :
  441.        SendMessage(HWindow, WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(pos.x, pos.y));
  442.        SendMessage(HWindow, WM_LBUTTONUP, MK_LBUTTON, MAKELONG(pos.x, pos.y));
  443.        break;
  444.   }
  445.   /* Update position of cursor in window with clipping */
  446.   if (x < 0 )                 x = gridSize / 2;
  447.   if (y < 0 )                 y = gridSize / 2;
  448.   if (x > cols * gridSize )   x = (Attr.W - gridSize) / 2;
  449.   if (y > rows * gridSize )   y = (Attr.H - gridSize) / 2;
  450.   pos.x = x;
  451.   pos.y = y;
  452.   ClientToScreen(HWindow, &pos);
  453.   SetCursorPos(pos.x, pos.y);
  454. }
  455.  
  456. /* Capture mouse movement when the left button is pressed. A display
  457.    context is taken; it is freed in the WMLButtonUp method. */
  458. void TLifeWindow::WMLButtonDown(TMessage& ) {
  459.   if (!mouseDown ) {
  460.     xDown = -1;     /* sentinel values to track movement */
  461.     yDown = -1;
  462.     mouseDown = True;
  463.     mouseMoveDC = GetDC(HWindow);
  464.     SelectObject(mouseMoveDC, GetStockObject(WHITE_PEN));
  465.   }
  466. }
  467.  
  468. /* Update the cells as the mouse is dragged */
  469. void TLifeWindow::WMMouseMove(TMessage& Msg) {
  470.   int xScreen, yScreen, x, y;
  471.   Boolean state;
  472.   if (mouseDown ) {
  473.     /* determine where clicked */
  474.     xScreen = LOWORD(Msg.LParam);
  475.     yScreen = HIWORD(Msg.LParam);
  476.     /* translate into cell coordinates */
  477.     x = xScreen / gridSize;
  478.     y = yScreen / gridSize;
  479.     if ((x != xDown) || (y != yDown) ) {     /* a new position */
  480.       /* Invert the cell's state, then redraw */
  481.       xDown = x;                             /* store position */
  482.       yDown = y;
  483.       state = (Boolean)!(cells.aliveCell(x, y));
  484.       cells.setCell(x, y, state);
  485.       cells.drawCell(mouseMoveDC, x, y, state);
  486.     }
  487.   }
  488. }
  489.  
  490. /* Stop capturing mouse movement when mouse is released */
  491. void TLifeWindow::WMLButtonUp(TMessage& Msg) {
  492.   // force drawing in same spot:
  493.   TLifeWindow::WMMouseMove(Msg);
  494.   if (mouseDown ) {
  495.     mouseDown = False;
  496.     SelectObject(mouseMoveDC, GetStockObject(BLACK_PEN));
  497.     ReleaseDC(HWindow, mouseMoveDC);
  498.   }
  499. }
  500.  
  501. /* update internal information when resizing then redraw */
  502. void TLifeWindow::WMSize(TMessage& Msg) {
  503.   cells.C_rows = rows = HIWORD(Msg.LParam) / gridSize;
  504.   cells.C_cols = cols = LOWORD(Msg.LParam) / gridSize;
  505.   Attr.H = HIWORD(Msg.LParam);
  506.   Attr.W = LOWORD(Msg.LParam);
  507.   InvalidateRect(HWindow, 0, True);
  508. }
  509.  
  510. // The application defines startup behavior for the window:
  511. class TLifeApplication : public TApplication {
  512. public:
  513.   TLifeApplication (LPSTR Name, HANDLE hInstance, HANDLE hPrevInstance,
  514.     LPSTR lpCmdLine, int nCmdShow)
  515.       : TApplication(Name, hInstance, hPrevInstance,
  516.              lpCmdLine, nCmdShow) {};
  517.   virtual void InitInstance() {
  518.     TApplication::InitInstance();  // call base class version
  519.     // Load the accelerator table for hotkeys:
  520.     HAccTable = LoadAccelerators(hInstance, "LifeKeys");
  521.   }
  522.   /* Start the main window */
  523.   virtual void InitMainWindow() {
  524.     MainWindow = new TLifeWindow(0, "CLife");
  525.   }
  526. };
  527.  
  528. /*--------------------------------------------------*/
  529. /* Main program:                                    */
  530. /*--------------------------------------------------*/
  531.  
  532. int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance,
  533.        LPSTR lpCmdLine, int nCmdShow) {
  534.   TLifeApplication MyApp("CLife",hInstance,
  535.                hPrevInstance,lpCmdLine, nCmdShow);
  536.   MyApp.Run();
  537.   return MyApp.Status;
  538. }
  539.